package sw6.admin.gui.logic;

import sw6.admin.R;
import sw6.admin.database.DatabaseHelper;
import sw6.admin.gui.StateDialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

/**
 * This class builds the GUI dialogs for settings, and returns a list of MenuEntries.
 * The dialogs are used by the user to manipulate the value of settings.
 * The class is used by calling BuildMenu, giving the method a Menu paratemter, witch serves
 * as the root of a tree structure representing the settings and its sub-menu structure.
 * BuildMenu then loops through each setting in Menu, and builds a dialog
 * for each setting (except for boolean, object and stdobject settings).
 * 
 *  - The dialog for integer settings are build using the setupIntegerOrDoubleInputDialog(setting) method.
 *  - The dialog for double settings are build using the setupIntegerOrDoubleInputDialog(setting) method.
 *  - The dialog for string settings are build using the setupStringInputDialog(setting) method.
 * 	- The dialog for enum settings are build using the setupEnumDialog(setting) method.
 *  - No dialog for boolean settings is created. The value of boolean settings are changed from the menu inteface (MenuActivity)
 *  - No dialog for object settings is created. The gui for changing the value of objects, is handled by app developers
 *  - No dialog for stdobject settings is created. The guid for changing the value of stdobjects are handled by sw6.admin.gui.types
 *  
 * @author sw6b
 * 
 */
public class GuiBuilder {
	private final int COLOR_RED = Color.argb(255, 255, 000, 000);
	private final int COLOR_WHITE = Color.argb(255, 255, 255, 255);
	private final int COLOR_BLACK = Color.argb(255, 0, 0, 0);

	private Context mContext;
	private String mPackageName;

	/**
	 * Builds the dialogs for the menu
	 * @param menu Holds information about settings in the menu.
	 * @param context The context the gui components should be used in.
	 */
	public void buildDialogs(Menu menu, final Context context) {
		mContext = context;
		mPackageName = menu.mPackageName;

		// Builds the dialogs for each setting, and creates a menu entry for each setting
		// A menu entry is class containing the information that will be displayed on the GUI menu list
		for (Setting setting : menu.getSettings()) {
			// Creates an integer-double dialog
			if (setting.isInteger() || setting.isDouble()) {
				setupIntegerOrDoubleInputDialog(setting);
			}
			// Creates a string dialog
			if (setting.isString()) {
				setupStringInputDialog(setting);
			}
			// Creates an enum dialog
			else if (setting.isEnum()) {
				setupEnumDialog(setting);
			}
			else if (setting.isBoolean()) {
				// Seting up the boolean interface is done in MenuActivity.
			}
		}
	}


	/**
	 * Creates the dialog for enum settings. The dialog gives the user a way of changing the
	 * value of an enum
	 * @param setting The enum setting
	 */
	private void setupEnumDialog(final Setting setting) {

		// Creates a list of the names of all enum elements in the enum
		CharSequence[] items = new CharSequence[setting.getEnumElements().size()];
		for (int i = 0; i < setting.getEnumElements().size(); i++) {
			SettingEnumElement see = setting.getEnumElements().get(i);
			items[i] = see.getmRealName();
		}

		// Creates the dialog builder that i use to create the enum dialog
		AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
		builder.setTitle(setting.getRealName());

		// I need to get the index of the current selected enum in the database.
		// I do that by finding the SettingEnumElement in setting, whos enum value corresponds to the selected enum, and use that index
		
		DatabaseHelper db = new DatabaseHelper(mContext);
		
		int enumInteger = db.getEnum(mPackageName, setting.getVarName()).getInt(0);
		int enumIndex = -1;

		db.close();
		
		for (int i = 0; i < setting.getEnumElements().size(); i++) {
			SettingEnumElement see = setting.getEnumElements().get(i);
			if (see.getDatabaseInteger() == enumInteger) {
				enumIndex = i;
			}
		}

		// When an enum element is selceted in the gui, the choise is saved in the database, and the enum dialog i closed
		builder.setSingleChoiceItems(items, enumIndex, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int item) {
				DatabaseHelper db = new DatabaseHelper(mContext);
				SettingEnumElement see = setting.getEnumElements().get(item);
				int enumValue = see.getDatabaseInteger();
				db.updateEnum(mPackageName, setting.getVarName(), enumValue);
				db.close();

				dialog.dismiss();
			}
		});
		AlertDialog dialog = builder.create();

		setting.setDialog(dialog);
	}
	
	/**
	 * Builds the dialog for a string setting
	 * @param setting The string setting
	 */
	private void setupStringInputDialog(final Setting setting) {
		final StateDialog dialog = new StateDialog(mContext);
		dialog.setContentView(R.layout.input_dialog);

		final Button okButton = (Button) dialog.findViewById(R.id.ok);
		final TextView minText = (TextView) dialog.findViewById(R.id.min);
		final TextView maxText = (TextView) dialog.findViewById(R.id.max);
		final EditText editText = (EditText) dialog.findViewById(R.id.editText);

		//editText.setMovementMethod(new ScrollingMovementMethod());

		// If the text is changed in editText, we check if the the text is longer than max
		editText.addTextChangedListener(new TextWatcher() {

			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {
				int textLength = editText.getText().toString().length();
				if (textLength == 0)
					return;
				if (setting.isLargerIntMin(textLength)) {
					maxText.setTextColor(COLOR_RED);
					okButton.setEnabled(false);
				}
				else {
					editText.setTextColor(COLOR_BLACK);
					minText.setTextColor(COLOR_WHITE);
					maxText.setTextColor(COLOR_WHITE);
					okButton.setEnabled(true);
				}
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count, int after) {
			}

			@Override
			public void afterTextChanged(Editable s) {
			}
		});

		// When the dialog is shown, I load the setting value from the database and insert it into editText
		dialog.setOnShowListener(new DialogInterface.OnShowListener() { 
			@Override
			public void onShow(DialogInterface dialogInterface) {

				if (dialog.getRestored() == true) {
					return;
				}
				if (setting.isString()) {
					DatabaseHelper dh = new DatabaseHelper(mContext);
					String databaseValue = dh.getString(mPackageName, setting.getVarName()).getString(0);
					dh.close();
					
					editText.setText(databaseValue);
				}

				resetEditTextCurser(editText);
			}
		});

		dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface dialog) {
				DatabaseHelper dh = new DatabaseHelper(mContext);
				if (setting.isInteger()) {
					int value = dh.getInteger(mPackageName, setting.getVarName()).getInt(0);
					editText.setText(Integer.toString(value));
				}
				if (setting.isDouble()) {
					double value = dh.getDouble(mPackageName, setting.getVarName()).getDouble(0);
					editText.setText(Double.toString(value));
				}
				if (setting.isString()) {
					String value = dh.getString(mPackageName, setting.getVarName()).getString(0);
					editText.setText(value);
				}
				dh.close();
			}
		});


		setupButtons(setting, dialog);
		setupMinMax(setting, dialog);

		setupDescriptionAndTitle(setting, dialog);
	}

	/**
	 * Builds the dialog for an integer or double setting
	 * @param setting A integer or double setting
	 */
	private void setupIntegerOrDoubleInputDialog(final Setting setting) {
		final StateDialog dialog = new StateDialog(mContext);
		dialog.setContentView(R.layout.input_dialog);

		final Button okButton = (Button) dialog.findViewById(R.id.ok);
		final TextView minText = (TextView) dialog.findViewById(R.id.min);
		final TextView maxText = (TextView) dialog.findViewById(R.id.max);
		final EditText editText = (EditText) dialog.findViewById(R.id.editText);
		final Setting finalSetting = setting;

		//editText.setMovementMethod(new ScrollingMovementMethod());

		if (setting.isDouble() == true) {
			editText.setKeyListener(SignedDecimalKeyListener.getInstance());
		}
		if (setting.isInteger() == true) {
			editText.setKeyListener(SignedIntegerKeyListener.getInstance());
		}

		// If the text is changed in editText, we check if the the value is allowed by the min max constraint
		editText.addTextChangedListener(new TextWatcher() {
			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {
				String text = editText.getText().toString();
				if (text.equals("") || text.equals("-"))
					return;
				if (text.contains(".-") || text.contains("-."))
					return;

				// if bound remains false, the current value in editText
				boolean bound = false;
				if (finalSetting.isInteger()){
					int textValue;
					try {
						textValue = Integer.parseInt(editText.getText().toString());
					}
					catch (NumberFormatException e) {
						removeLastCharOfEditText(editText);
						return;
					}

					if (finalSetting.isSmallerIntMin(textValue)) {
						minText.setTextColor(COLOR_RED);
						bound = true;
					}
					if (finalSetting.isLargerIntMin(textValue)) {
						maxText.setTextColor(COLOR_RED);
						bound = true;
					}
				}
				else if (finalSetting.isDouble()){
					if (text.startsWith(".") == true) {
						return;
					}
					double textValue;
					try {
						textValue = Double.parseDouble(editText.getText().toString());
					}
					catch (NumberFormatException e) {
						removeLastCharOfEditText(editText);
						return;
					}

					if (finalSetting.isSmallerDoubleMin(textValue)) {
						minText.setTextColor(COLOR_RED);
						bound = true;
					}
					if (finalSetting.isLargerDoubleMin(textValue)) {
						maxText.setTextColor(COLOR_RED);
						bound = true;
					}
				}
				if (bound == false){
					okButton.setEnabled(true);
					editText.setTextColor(COLOR_BLACK);
					minText.setTextColor(COLOR_WHITE);
					maxText.setTextColor(COLOR_WHITE);
				}
				else {
					editText.setTextColor(COLOR_RED);
					okButton.setEnabled(false);
				}
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count, int after) {
			}

			@Override
			public void afterTextChanged(Editable s) {
			}
		});

		// When the dialog is shown, I load the setting value from the database and insert it into editText
		dialog.setOnShowListener(new DialogInterface.OnShowListener() { 
			@Override
			public void onShow(DialogInterface dialogInterface) {
				if (dialog.getRestored() == true) {
					return;
				}
				
				DatabaseHelper dh = new DatabaseHelper(mContext);
				if (finalSetting.isInteger()) {
					int databaseValue = dh.getInteger(mPackageName, setting.getVarName()).getInt(0);
					editText.setText(Integer.toString(databaseValue));
				}
				if (finalSetting.isDouble()) {
					double databaseValue = dh.getDouble(mPackageName, setting.getVarName()).getDouble(0);
					editText.setText(Double.toString(databaseValue));
				}

				dh.close();
				
				resetEditTextCurser(editText);
			}
		});

		dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface dialog) {
				
				DatabaseHelper dh = new DatabaseHelper(mContext);
				
				if (setting.isInteger()) {
					int value = dh.getInteger(mPackageName, setting.getVarName()).getInt(0);
					editText.setText(Integer.toString(value));
				}
				if (setting.isDouble()) {
					double value = dh.getDouble(mPackageName, setting.getVarName()).getDouble(0);
					editText.setText(Double.toString(value));
				}
				if (setting.isString()) {
					String value = dh.getString(mPackageName, setting.getVarName()).getString(0);
					editText.setText(value);
				}
				
				dh.close();
			}
		});

		setupButtons(setting, dialog);
		setupMinMax(setting, dialog);
		setupDescriptionAndTitle(setting, dialog);
	}

	/**
	 * Places the curser of an EditText at the end of the the text in the edtiText
	 * @param editText The editText who needs its curser position reset
	 */
	private void resetEditTextCurser(EditText editText) {
		editText.setSelection(editText.getText().toString().length());
		editText.setSelected(true);
	}

	/**
	 * Removes the last charachter in editText.
	 * @param editText The EditText whos last charachter is to be removed
	 */
	private void removeLastCharOfEditText(EditText editText) {
		String text = editText.getText().toString();
		text = text.substring(0, text.length()-1);
		editText.setText(text);
		resetEditTextCurser(editText);
	}

	/**
	 * Sets the description and title in the dialog
	 * @param setting The setting from where the description and title will be read
	 * @param dialog The description and title of the dialog will be set
	 */
	private void setupDescriptionAndTitle(Setting setting, Dialog dialog) {
		TextView descText = (TextView) dialog.findViewById(R.id.desc);
		descText.setText(setting.getDescription());

		dialog.setTitle(setting.getRealName());
		setting.setDialog(dialog);
	}

	/**
	 * Sets the min and max textFields
	 * @param setting The setting from where the min and max values will be read
	 * @param dialog The min and max TextFields of the dialog will be set
	 */
	private void setupMinMax(Setting setting, final Dialog dialog) {
		final TextView minText = (TextView) dialog.findViewById(R.id.min);
		final TextView maxText = (TextView) dialog.findViewById(R.id.max);

		if (setting.isInteger())
		{
			String minString = "";
			String maxString = "";
			if (setting.isMinSet()) {
				minString = "Min: " + setting.getIntMin();
			}
			if (setting.isMaxSet()) {
				maxString = "Max: " + setting.getIntMax();
			}

			minText.setText(minString);
			maxText.setText(maxString);
		}
		else if (setting.isDouble()) {
			String minString = "";
			String maxString = "";
			if (setting.isMinSet()) {
				minString = "Min: " + setting.getDoubleMin();
			}
			if (setting.isMaxSet()) {
				maxString = "Max: " + setting.getDoubleMax();
			}

			minText.setText(minString);
			maxText.setText(maxString);
		}
	}

	/**
	 * Creates event listeners for the ok and fortryd buttons. When the ok button is pressed
	 * the new values given by the user via the dialog interface will be updated in the database.
	 * When the fortryd button is pressed, the dialog i simply dismissed.
	 * @param setting Used when having to update variables in the database
	 * @param dialog The dialog containing the buttons that are to be setup
	 */
	private void setupButtons(final Setting setting, final Dialog dialog) {
		Button okButton = (Button) dialog.findViewById(R.id.ok);
		Button fortrydButton = (Button)dialog.findViewById(R.id.cancel);
		final EditText editText = (EditText) dialog.findViewById(R.id.editText);

		// When the OK button is presesd, I save the user inserted value into the database, and close the dialog
		okButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {

				String text = editText.getText().toString();

				if (text.equals("") || text.startsWith("-")|| text.startsWith(".") || text.contains("-.") || text.contains(".-"))
				{
					return;
				}

				DatabaseHelper db = new DatabaseHelper(mContext);
				if (setting.isInteger()) {
					int value = Integer.parseInt(editText.getText().toString());
					db.updateInteger(mPackageName, setting.mVarName, value);
				}
				if (setting.isDouble()) {
					double value = Double.parseDouble(editText.getText().toString());
					db.updateDouble(mPackageName, setting.mVarName, value);
				}
				if (setting.isString()) {
					String value = editText.getText().toString();
					db.updateString(mPackageName, setting.mVarName, value);
				}
				db.close();

				dialog.dismiss();
			} 
		});

		// When the fortyd button is pressed, I close the dialog
		fortrydButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				dialog.dismiss();
			} 
		});
	}
}

